package com.google.code.b0rx0r.ui.model;

import java.util.ArrayList;
import java.util.List;

import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import com.google.code.b0rx0r.program.Program;
import com.google.code.b0rx0r.program.ProgramListener;
import com.google.code.b0rx0r.program.ProgramManager;
import com.google.code.b0rx0r.program.ProgramManagerListener;
import com.google.code.b0rx0r.program.Sample;
import com.google.code.b0rx0r.program.Sequence;
import com.google.code.b0rx0r.program.Slice;
import com.google.code.b0rx0r.program.Triggerable;
import com.google.code.b0rx0r.program.TriggerableListener;

public class ProgramTriggerableListTreeModel implements TreeModel, ProgramManagerListener, ProgramListener, TriggerableListener {
	private static final String[] HEADERS = new String[] {Messages.getString("ProgramTriggerableListTreeModel.Header.Samples"), Messages.getString("ProgramTriggerableListTreeModel.Header.Slices"), Messages.getString("ProgramTriggerableListTreeModel.Header.Sequences")}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

	private ProgramManager programManager;
		private List<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
		
		public ProgramTriggerableListTreeModel(ProgramManager programManager) {
			this.programManager = programManager;
			programManager.addProgramManagerListener(this);
			
			programChanged(null, programManager.getProgram());
		}

		@Override
		public void programChanged(Program oldProgram, Program newProgram) {
			if (oldProgram != null)
				oldProgram.removeProgramListener(this);
			
			if (newProgram != null)
				newProgram.addProgramListener(this);
			
			fireTreeStructureChanged();
		}

		
		@Override
		public void sampleAdded(Sample sample) {
			sample.addTriggerableListener(this);
			fireTreeStructureChanged(this, HEADERS[0]);
		}

		@Override
		public void sampleRemoved(Sample sample) {
			sample.removeTriggerableListener(this);
			fireTreeStructureChanged(this, HEADERS[0]);
		}

		@Override
		public void sliceAdded(Slice slice) {
			slice.addTriggerableListener(this);
			fireTreeStructureChanged(this, HEADERS[1]);
		}

		@Override
		public void sliceRemoved(Slice slice) {
			slice.removeTriggerableListener(this);
			fireTreeStructureChanged(this, HEADERS[1]);
		}

		@Override
		public void sequenceAdded(Sequence sequence) {
			sequence.addTriggerableListener(this);
			fireTreeStructureChanged(this, HEADERS[2]);
		}

		@Override
		public void sequenceRemoved(Sequence sequence) {
			sequence.removeTriggerableListener(this);
			fireTreeStructureChanged(this, HEADERS[2]);
		}

		
		
		
		
		
		
		
		
		
		
		private void fireTreeStructureChanged() {
			fireTreeStructureChanged(this);
		}
		
		private void fireTreeStructureChanged(Object... path) {
			TreeModelEvent e = new TreeModelEvent(this, new TreePath(path));

			for (TreeModelListener listener : listeners) {
				listener.treeStructureChanged(e);
			}
		}

		@Override
		public void addTreeModelListener(TreeModelListener l) {
			listeners.add(l);
		}

		@Override
		public void removeTreeModelListener(TreeModelListener l) {
			listeners.remove(l);
		}
		
		@Override
		public Object getChild(Object parent, int index) {
			if (parent == this) {
				return HEADERS[index];
			}
			
			if (parent == HEADERS[0]) {
				return programManager.getProgram().getSamples().get(index);
			}
			if (parent == HEADERS[1]) {
				return programManager.getProgram().getSlices().get(index);
			}
			if (parent == HEADERS[2]) {
				return programManager.getProgram().getSequences().get(index);
			}
			
			return null;
		}

		@Override
		public int getChildCount(Object parent) {
			if (parent == this) {
				return 3;
			}
			
			if (programManager.getProgram() == null) 
				return 0;
			
			if (parent == HEADERS[0]) {
				return programManager.getProgram().getSamples().size();
			}
			if (parent == HEADERS[1]) {
				return programManager.getProgram().getSlices().size();
			}
			if (parent == HEADERS[2]) {
				//TODO sequences
				return programManager.getProgram().getSequences().size();
			}
			
			return 0;
		}


		@Override
		public Object getRoot() {
			return this;
		}

		@Override
		public boolean isLeaf(Object node) {
			if (node == this)
				return false;
			
			if (programManager.getProgram() == null)
				return true;
			
			if(node instanceof Triggerable)
				return true;
			
			return false;
		}


		@Override
		public int getIndexOfChild(Object parent, Object child) {
			if (parent == null)
				return -1;
			
			if (child == null)
				return -1;
			
			if (parent == this) {
				for (int i=0; i<HEADERS.length; i++) {
					if (HEADERS[i].equals(child))
						return i;
				}
				
				return -1;
			}
			
			
			
			if (parent instanceof String) {
				if (parent == HEADERS[0]) {
					return programManager.getProgram().getSamples().indexOf(child);
				}
				if (parent == HEADERS[1]) {
					return programManager.getProgram().getSlices().indexOf(child);
				}
				if (parent == HEADERS[2]) {
					return programManager.getProgram().getSequences().indexOf(child);
				}
			}

		
		
			return -1;
		}
		
		@Override
		public void valueForPathChanged(TreePath path, Object newValue) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void triggerableChanged(Triggerable triggerable) {
			fireValueChange(triggerable);
		}

		private void fireValueChange(Triggerable triggerable) {
			TreePath tp = getParentPathTo(triggerable);
			TreeModelEvent e = new TreeModelEvent(this, tp, new int[]{getIndexOfChild(tp.getLastPathComponent(), triggerable)}, new Object[]{triggerable});
			for (TreeModelListener l : listeners) {
				l.treeNodesChanged(e);
			}
		}

		private TreePath getParentPathTo(Triggerable triggerable) {
			Object[] path = new Object[2];
			
			path[0] = this;
			
			int index = 0;
			if (triggerable instanceof Sample) {
				index = 0;
			} else if (triggerable instanceof Slice) {
				index = 1;
			} else if (triggerable instanceof Sequence) {
				index = 2;
			}
			
			path[1] = HEADERS[index];

			return new TreePath(path);
		}

		
		
		
		
	
}
